{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3",
   "language": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "source": [
    "先使用vgg16的卷积基，然后添加自己的密集分类层   \n",
    "首先是数据划分，打乱顺序和重命名之后按照3：1：1的比例分成train、validaiton、test三个文件夹  \n",
    "keras会自己根据文件夹发现有多少个类别，并自动执行多元交叉验证  \n",
    "\n",
    "## 主要步骤\n",
    "> 1.加载VGG16模型（不包含密集连接层，因为我们要用自己的四分类）  \n",
    "> 2.打乱源文件并按上述比例分配好供keras使用  \n",
    "> 3.根据VGG16模型的最后输出写自己的四分类密集连接分类器  \n",
    "> 4.将VGG16模型冻结，用自己的数据训练密集分类器  \n",
    "> 5.画出训练精度验证精度和训练损失验证损失"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras import models \n",
    "from keras import layers \n",
    "from keras.applications import VGG16 \n",
    "\n",
    "conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(150, 150, 3)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将原文件夹下的图片重新命名\n",
    "import os \n",
    "import shutil \n",
    "import random \n",
    "\n",
    "def rename(path):\n",
    "    file_list = os.listdir(path) \n",
    "    file_num = len(file_list) \n",
    "    random_num = random.sample(range(0,file_num), file_num)  # 生成(0,file_num)范围内file_num个不重复的随机数\n",
    "    \n",
    "    i = 0 \n",
    "    for files in file_list:\n",
    "        old_dir_path = os.path.join(path, files) \n",
    "\n",
    "        file_name = os.path.splitext(files)[0] \n",
    "        file_type = os.path.splitext(files)[1] \n",
    "        \n",
    "        new_dir_path = os.path.join(path, str(random_num[i]) + file_type)\n",
    "        os.rename(old_dir_path, new_dir_path) \n",
    "        i += 1\n",
    "\n",
    "path = r'D:\\BaiduNetdiskDownload\\DLdata\\garbage_classify_demo' \n",
    "\n",
    "rename(path+'\\\\0')\n",
    "rename(path+'\\\\1')\n",
    "rename(path+'\\\\2')\n",
    "rename(path+'\\\\3') "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据预处理，将源文件夹下的0123四个文件夹下的图片，分成train、validation、test\n",
    "\n",
    "# 创建文件夹结构\n",
    "original_dataset_dir = r'D:\\BaiduNetdiskDownload\\DLdata\\garbage_classify_demo' \n",
    "base_dir = r'D:\\BaiduNetdiskDownload\\DLdata\\garbage_classify'\n",
    "shutil.rmtree(base_dir) \n",
    "os.mkdir(base_dir) \n",
    "\n",
    "train_dir = os.path.join(base_dir, 'train') \n",
    "os.mkdir(train_dir) \n",
    "validation_dir = os.path.join(base_dir, 'validation') \n",
    "os.mkdir(validation_dir) \n",
    "test_dir = os.path.join(base_dir, 'test') \n",
    "os.mkdir(test_dir) \n",
    "\n",
    "\n",
    "# 统计每个种类垃圾的图片文件个数，打乱文件，之后按照train:validation:test == 3:1:1的比例分别放到对应文件夹\n",
    "dir_path = ['0', '1', '2', '3'] \n",
    "number_of_file = [0, 0, 0, 0]\n",
    "numbers = []\n",
    "for path in dir_path:\n",
    "    files = os.listdir(os.path.join(original_dataset_dir, path)) \n",
    "    num_img = len(files) \n",
    "    number_of_file[int(path)] = num_img \n",
    "    temp = num_img // 5\n",
    "    numbers.append([0, 3*temp, 4*temp, 5*temp])\n",
    "\n",
    "\n",
    "dst_dirs = [train_dir, validation_dir, test_dir] \n",
    "\n",
    "for i in range(3):\n",
    "    dir = dst_dirs[i]\n",
    "    for j in range(len(numbers)):\n",
    "        num = numbers[j]\n",
    "        temp_dir = os.path.join(dir, str(j)) \n",
    "        os.mkdir(temp_dir) \n",
    "        for k in range(num[i], num[i+1]):\n",
    "            src = os.path.join(original_dataset_dir+'\\\\'+str(j), str(k)+'.jpg')\n",
    "            dst = os.path.join(temp_dir, str(k)+'.jpg')\n",
    "            shutil.copyfile(src, dst)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Found 2061 images belonging to 4 classes.\n",
      "Found 687 images belonging to 4 classes.\n",
      "Found 687 images belonging to 4 classes.\n"
     ]
    }
   ],
   "source": [
    "# 使用预训练的卷积基提取特征\n",
    "import os \n",
    "import numpy as np \n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "base_dir = r'D:\\BaiduNetdiskDownload\\DLdata\\garbage_classify'\n",
    "train_dir = os.path.join(base_dir, 'train') \n",
    "validation_dir = os.path.join(base_dir, 'validation') \n",
    "test_dir = os.path.join(base_dir, 'test') \n",
    "\n",
    "datagen = ImageDataGenerator(rescale=1./255)  # 缩放比率为1/255\n",
    "batch_size = 5\n",
    "\n",
    "def extract_features(directory, sample_count):\n",
    "    features = np.zeros(shape=(sample_count, 4, 4, 512))  # (4,4,512)是VGG16模型的最后输出\n",
    "    labels = np.zeros(shape=(sample_count, 4)) \n",
    "    generator = datagen.flow_from_directory( \n",
    "        directory, \n",
    "        target_size=(150, 150), \n",
    "        batch_size=batch_size, \n",
    "        class_mode='categorical' \n",
    "    )\n",
    "    i = 0 \n",
    "    for inputs_batch, labels_batch in generator: \n",
    "        features_batch = conv_base.predict(inputs_batch) \n",
    "        features[i*batch_size : (i+1)*batch_size] = features_batch\n",
    "        labels[i*batch_size : (i+1)*batch_size] = labels_batch\n",
    "        i += 1\n",
    "        if (i+1)*batch_size >= sample_count:\n",
    "            break \n",
    "    return features, labels \n",
    "\n",
    "train_features, train_labels = extract_features(train_dir, 2061) \n",
    "validation_features, validation_labels = extract_features(validation_dir, 687) \n",
    "test_features, test_labels = extract_features(test_dir, 687) \n",
    "\n",
    "# 目前提取的特征形状为(samples,4,4,512)，要将其输入到密集连接分类器中，必须先展平为(samples, 8192)\n",
    "train_features = np.reshape(train_features, (train_features.shape[0], 4 * 4 * 512)) \n",
    "validation_features = np.reshape(validation_features, (validation_features.shape[0], 4 * 4 * 512)) \n",
    "test_features = np.reshape(test_features, (test_features.shape[0], 4 * 4 * 512)) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Epoch 1/20\n",
      "104/104 [==============================] - 4s 32ms/step - loss: 1.3698 - acc: 0.4113 - val_loss: 0.7635 - val_acc: 0.7089\n",
      "Epoch 2/20\n",
      "104/104 [==============================] - 3s 33ms/step - loss: 0.8240 - acc: 0.6628 - val_loss: 0.6142 - val_acc: 0.7656\n",
      "Epoch 3/20\n",
      "104/104 [==============================] - 3s 27ms/step - loss: 0.6432 - acc: 0.7513 - val_loss: 0.5390 - val_acc: 0.8253\n",
      "Epoch 4/20\n",
      "104/104 [==============================] - 3s 31ms/step - loss: 0.5443 - acc: 0.7921 - val_loss: 0.4829 - val_acc: 0.8311\n",
      "Epoch 5/20\n",
      "104/104 [==============================] - 3s 31ms/step - loss: 0.5057 - acc: 0.8184 - val_loss: 0.4566 - val_acc: 0.8268\n",
      "Epoch 6/20\n",
      "104/104 [==============================] - 3s 27ms/step - loss: 0.4563 - acc: 0.8447 - val_loss: 0.4356 - val_acc: 0.8384\n",
      "Epoch 7/20\n",
      "104/104 [==============================] - 3s 28ms/step - loss: 0.4039 - acc: 0.8563 - val_loss: 0.4073 - val_acc: 0.8675\n",
      "Epoch 8/20\n",
      "104/104 [==============================] - 4s 35ms/step - loss: 0.3677 - acc: 0.8662 - val_loss: 0.3914 - val_acc: 0.8748\n",
      "Epoch 9/20\n",
      "104/104 [==============================] - 3s 29ms/step - loss: 0.3524 - acc: 0.8703 - val_loss: 0.3825 - val_acc: 0.8675\n",
      "Epoch 10/20\n",
      "104/104 [==============================] - 3s 29ms/step - loss: 0.3274 - acc: 0.8879 - val_loss: 0.3722 - val_acc: 0.8719\n",
      "Epoch 11/20\n",
      "104/104 [==============================] - 3s 26ms/step - loss: 0.3064 - acc: 0.9040 - val_loss: 0.3661 - val_acc: 0.8763\n",
      "Epoch 12/20\n",
      "104/104 [==============================] - 4s 38ms/step - loss: 0.2985 - acc: 0.8954 - val_loss: 0.3620 - val_acc: 0.8792\n",
      "Epoch 13/20\n",
      "104/104 [==============================] - 4s 37ms/step - loss: 0.2436 - acc: 0.9256 - val_loss: 0.3542 - val_acc: 0.8748\n",
      "Epoch 14/20\n",
      "104/104 [==============================] - 3s 28ms/step - loss: 0.2266 - acc: 0.9332 - val_loss: 0.3605 - val_acc: 0.8690\n",
      "Epoch 15/20\n",
      "104/104 [==============================] - 3s 30ms/step - loss: 0.2324 - acc: 0.9291 - val_loss: 0.3529 - val_acc: 0.8777\n",
      "Epoch 16/20\n",
      "104/104 [==============================] - 3s 31ms/step - loss: 0.2075 - acc: 0.9368 - val_loss: 0.3438 - val_acc: 0.8821\n",
      "Epoch 17/20\n",
      "104/104 [==============================] - 3s 27ms/step - loss: 0.2168 - acc: 0.9301 - val_loss: 0.3444 - val_acc: 0.8836\n",
      "Epoch 18/20\n",
      "104/104 [==============================] - 3s 30ms/step - loss: 0.1942 - acc: 0.9373 - val_loss: 0.3344 - val_acc: 0.8865\n",
      "Epoch 19/20\n",
      "104/104 [==============================] - 3s 27ms/step - loss: 0.1756 - acc: 0.9501 - val_loss: 0.3336 - val_acc: 0.8923\n",
      "Epoch 20/20\n",
      "104/104 [==============================] - 3s 28ms/step - loss: 0.1762 - acc: 0.9532 - val_loss: 0.3349 - val_acc: 0.8879\n"
     ]
    }
   ],
   "source": [
    "# 定义并训练密集连接分类器\n",
    "from keras import optimizers\n",
    "\n",
    "model = models.Sequential() \n",
    "model.add(layers.Dense(256, activation='relu', input_dim=4*4*512)) \n",
    "# model.add(layers.Dense(64, activation='relu')) \n",
    "model.add(layers.Dropout(0.5)) \n",
    "model.add(layers.Dense(4, activation='softmax'))  # 对应四种垃圾：有害(电池)、可回收、厨余(湿)、其他(干垃圾吧)\n",
    "\n",
    "# 冻结卷积基，如果不这么做，那么卷积基之前学到的表示将会在训练过程中被修改\n",
    "conv_base.trainable = False \n",
    "\n",
    "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0, 1\"\n",
    "\n",
    "model.compile(\n",
    "    optimizers.RMSprop(lr=2e-5), \n",
    "    loss='categorical_crossentropy', \n",
    "    metrics=['acc']\n",
    ")\n",
    "\n",
    "history = model.fit(\n",
    "    train_features, \n",
    "    train_labels, \n",
    "    epochs=20,  # 设定的是100轮，但根据交叉验证损失图得：20轮时是最佳的，再往后validation loss会逐渐变大\n",
    "    batch_size=20, \n",
    "    validation_data = (validation_features, validation_labels)\n",
    ")\n",
    "\n",
    "model.save('output/initial_version.h5')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": "<Figure size 432x288 with 1 Axes>",
      "image/png": "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6rKjRo1i6dKlVFZW0qNHD6ZOnUphYSHHH388mzdvBuD666/nzjvvrC4/depURowYwRFHHMHf/x4m1vn00085//zzKSwsZOLEiRQXF7N06dL9Yrvhhhs45phjquPzaJTSt99+m6997WsUFhZSVFTEunXrAPj5z3/OUUcdRWFhIdMy+ZtRpIlLN2f0TzJbcbLl8WbMgE6dai7r1Cksz5hk3wDZuqVbo8/Ut2MysTX6VatWuZn5K6+8Ur1+69at7u6+e/duHzVqlC9fvtzd3UeOHOmvvfaa79692wF/8skn3d392muv9Ztuusnd3adNm+Z33HFHdfkf/ehH7u7+xBNP+Gmnnebu7jfddJN/5zvfcXf3pUuXeqtWrfy1117bL86qOPbu3esTJkyofr2ioiKfP3++u7vv3LnTP/30U58/f76PGjXKKyoqamx7IFSjl+Ym3ZzxwAPunTrV3LZTp/r9KshEKwTp1Oibm0Zp74px+OGHc8wxx1Q/nzNnDkVFRRQVFfHWW2+xYsX+P2A6duzI6aefDsDRRx9dXauOd9555+1X5qWXXmLChAkAFBYWMnjw4ITb/uUvf2HEiBEUFhby/PPPs3z5crZv386WLVs466yzgHCBU6dOnViwYAFXXHEFHTt2BKBnz571PxAizVS6OWPSJJg5EwYMALNwP3NmWJ6qSZNg3TrYuzfc12fbVDS50SvT1b9/+OmVaHlD6Ny5c/XjVatW8atf/YpXXnmFHj16cMkllyTsV96uXbvqx61bt6aysnK/MgDt27ffr4ynMFFMRUUFU6ZM4dVXX6Vv375cf/311XEk6gLp7uoaKS1WJnLGpEmZT86ZlHM1+kZp70rio48+omvXrnTr1o2NGzfyzDPPZPw1Ro0axbx58wB44403Ev5i2LlzJ61ataJ37958/PHHPProowDk5eXRu3dv/vCHPwDhQrSKigrGjh3L7373O3bu3AnAtm3bMh63SENKpx97NnNGY8m5RJ+Jn1EHqqioiEGDBjFkyBCuuuoqRo4cmfHXuOaaa3j//fcZOnQot912G0OGDKF79+41yvTq1YtLL72UIUOGcO6553LsscdWryspKeG2225j6NChjBo1ivLycs4880zGjRtHcXExw4YN44477sh43CK1yeYFR9nMGY2lyc0ZW1xc7PETj7z11lsceeSRWYqoaamsrKSyspIOHTqwatUqxo4dy6pVq2jTpmm0wulvJfVVlahj+6J36pR6ss3PT9z0MmBAaO9uKcxsiYeLU/eTczX6XPfJJ58wcuRICgsLOf/887n77rubTJKXliudGnmzuOComVOGaGZ69OjBkiVLsh2GSLX4GnlV0wmkViNPN1E3dgeM5kg1ehFJS7o18mZxwVEzp0QvImlJt0aebqJuCSdT06VELyJptbGnWyNvDhccNXdK9CI5IJvdEzPRdKJE3bCU6FMwevTo/S5+uvPOO/nOd75T63ZdunQBYMOGDVxwwQVJ9x3fnTTenXfeSUVMI+gZZ5zBhx9+mEro0gJkezx0NZ00fUr0KZg4cSJz586tsWzu3LlMnDgxpe2/+MUv8sgjjxzw68cn+ieffJIePXoc8P4ktzSF7omqkTdtSvQpuOCCC/jjH//I559/DsC6devYsGEDo0aN4pNPPmHMmDEUFRVx1FFH8cQTT+y3/bp16xgyZAgQhieYMGECQ4cO5aKLLqoedgDg29/+dvUQxzfccAMAv/71r9mwYQMnn3wyJ598MgD5+fls2bIFgNtvv50hQ4YwZMiQ6iGO161bx5FHHslVV13F4MGDGTt2bI3XqfKHP/yBY489luHDh3PKKaewadMmIPTVv/zyyznqqKMYOnRo9RAKTz/9NEVFRRQWFjJmzJiMHFtJXya6J9ZnuTQ/za4f/fe/DwmGX0/LsGEQ5ciEevXqxYgRI3j66acZP348c+fO5aKLLsLM6NChA4899hjdunVjy5YtHHfccZx99tlJBwn77W9/S6dOnVi2bBnLli2jqKioet2MGTPo2bMne/bsYcyYMSxbtozvfe973H777SxcuJDevXvX2NeSJUv43//9X/7xj3/g7hx77LF89atfJS8vj1WrVjFnzhzuueceLrzwQh599FEuueSSGtuPGjWKRYsWYWbce++93HLLLdx222389Kc/pXv37rzxxhsAbN++nfLycq666ipeeOEFCgoKNB5OE5JuP/IZMxJfmaruiblDNfoUxTbfxDbbuDs/+clPGDp0KKeccgrvv/9+dc04kRdeeKE64Q4dOpShQ4dWr5s3bx5FRUUMHz6c5cuXJxywLNZLL73EueeeS+fOnenSpQvnnXceL774IgAFBQUMGzYMSD4UcllZGaeddhpHHXUUv/zlL1m+fDkACxYs4Lvf/W51uby8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\n"
     },
     "metadata": {
      "needs_background": "light"
     }
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": "<Figure size 432x288 with 1 Axes>",
      "image/png": "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8jJ1eBGRrLRo0aKt7h63qW/GEnpeXh4LFy7M1OFFRLKSmSXs8awqFxGRHKGELiKSI5TQRURyhO5YJFJP7N27l6KiIj7//PNMhyJJaNKkCV26dKFx40RD+RxMCV2knigqKqJFixbk5eURBrmUusrd2bZtG0VFRXTv3r3yDSJZVeUyfTrk5UGDBuF5epVueyxSv33++ee0bdtWyTwLmBlt27at8q+prCmhT58O48fDrl1h+p13wjTAuBqPLydSPyiZZ4/q/K2ypoQ+ceL+ZF5q164wX0REsiihv/tu1eaLSN2ybds2+vXrR79+/ejQoQOdO3cum96zJ7lh0y+55BJWr15d4TpTpkxheorqYwcPHszSpUtTsq90yJoql65dQzVLvPkiknrTp4dfwO++G/7PJk2qWfVm27Zty5LjzTffTPPmzbn22msPWKfs7vUN4pc1H3300UqPc+WVV1Y/yCyXNSX0SZOgadMD5zVtGuaLSGqVXrN65x1w33/NqjYaIqxdu5bevXtzxRVXUFBQwKZNmxg/fjyFhYX06tWLW2+9tWzd0hJzcXExrVu3ZsKECfTt25fjjz+eDz/8EIAbbriB++67r2z9CRMmMGDAAI499lj+7//+D4DPPvuMb3/72/Tt25exY8dSWFhYaUl82rRp9OnTh969e3P99dcDUFxczIUXXlg2f/LkyQDce++99OzZk759+3LBBRek/JwlkjUJfdw4mDoVunUDs/A8daouiIrUhnRfs1q5ciWXXnopS5YsoXPnztx+++0sXLiQZcuW8dJLL7Fy5cqDttm5cydDhgxh2bJlHH/88TzyyCNx9+3uvPrqq9x1111lXw73338/HTp0YNmyZUyYMIElS5ZUGF9RURE33HADc+fOZcmSJfz73//mhRdeYNGiRWzdupXXX3+dN954g4suugiAO++8k6VLl7Js2TIeeOCBGp6d5GVNQoeQvDdsgH37wrOSuUjtSPc1q6OOOoqvfe1rZdMzZsygoKCAgoICVq1aFTehH3bYYYwcORKAr371q2zYsCHuvs8666yD1pk/fz7nnXceAH379qVXr14VxrdgwQJOOukk2rVrR+PGjTn//POZN28eX/7yl1m9ejU/+tGPmDNnDq1atQKgV69eXHDBBUyfPr1KHYNqKqsSuoikR6JrU7V1zapZs2Zlr9esWcOvfvUrXn75ZZYv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\n"
     },
     "metadata": {
      "needs_background": "light"
     }
    }
   ],
   "source": [
    "# 绘制结果\n",
    "import matplotlib.pyplot as plt \n",
    "\n",
    "acc = history.history['acc'] \n",
    "val_acc = history.history['val_acc'] \n",
    "loss = history.history['loss'] \n",
    "val_loss = history.history['val_loss'] \n",
    "\n",
    "epochs = range(1, len(acc) + 1) \n",
    "\n",
    "plt.plot(epochs, acc, 'bo', label='Training acc') \n",
    "plt.plot(epochs, val_acc, 'b', label='Validation acc') \n",
    "plt.title('Training and validation accuracy') \n",
    "plt.legend() \n",
    "\n",
    "plt.figure() \n",
    "\n",
    "plt.plot(epochs, loss, 'bo', label='Training loss') \n",
    "plt.plot(epochs, val_loss, 'b', label='Validation loss') \n",
    "plt.title('Training and validation loss') \n",
    "plt.legend() \n",
    "\n",
    "plt.show() "
   ]
  },
  {
   "source": [
    "## 接下来使用数据增强来训练模型\n"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Found 2061 images belonging to 4 classes.\n",
      "Found 687 images belonging to 4 classes.\n",
      "Epoch 1/100\n",
      "WARNING:tensorflow:Model was constructed with shape (None, 8192) for input KerasTensor(type_spec=TensorSpec(shape=(None, 8192), dtype=tf.float32, name='dense_21_input'), name='dense_21_input', description=\"created by layer 'dense_21_input'\"), but it was called on an input with incompatible shape (None, None, None, None).\n"
     ]
    },
    {
     "output_type": "error",
     "ename": "ValueError",
     "evalue": "in user code:\n\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:805 train_function  *\n        return step_function(self, iterator)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:795 step_function  **\n        outputs = model.distribute_strategy.run(run_step, args=(data,))\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:1259 run\n        return self._extended.call_for_each_replica(fn, args=args, kwargs=kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:2730 call_for_each_replica\n        return self._call_for_each_replica(fn, args, kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:3417 _call_for_each_replica\n        return fn(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:788 run_step  **\n        outputs = model.train_step(data)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:756 train_step\n        y, y_pred, sample_weight, regularization_losses=self.losses)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\compile_utils.py:203 __call__\n        loss_value = loss_obj(y_t, y_p, sample_weight=sw)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:152 __call__\n        losses = call_fn(y_true, y_pred)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:256 call  **\n        return ag_fn(y_true, y_pred, **self._fn_kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\util\\dispatch.py:201 wrapper\n        return target(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:1537 categorical_crossentropy\n        return K.categorical_crossentropy(y_true, y_pred, from_logits=from_logits)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\util\\dispatch.py:201 wrapper\n        return target(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\backend.py:4833 categorical_crossentropy\n        target.shape.assert_is_compatible_with(output.shape)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\framework\\tensor_shape.py:1134 assert_is_compatible_with\n        raise ValueError(\"Shapes %s and %s are incompatible\" % (self, other))\n\n    ValueError: Shapes (None, None) and (None, None, None, 4) are incompatible\n",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-55-2c35bbcf71d9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     31\u001b[0m     \u001b[0mepochs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m100\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     32\u001b[0m     \u001b[0mvalidation_data\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mvalidation_generator\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 33\u001b[1;33m     \u001b[0mvalidation_steps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     34\u001b[0m )\n\u001b[0;32m     35\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py\u001b[0m in \u001b[0;36mfit_generator\u001b[1;34m(self, generator, steps_per_epoch, epochs, verbose, callbacks, validation_data, validation_steps, validation_freq, class_weight, max_queue_size, workers, use_multiprocessing, shuffle, initial_epoch)\u001b[0m\n\u001b[0;32m   1859\u001b[0m         \u001b[0muse_multiprocessing\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0muse_multiprocessing\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1860\u001b[0m         \u001b[0mshuffle\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mshuffle\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1861\u001b[1;33m         initial_epoch=initial_epoch)\n\u001b[0m\u001b[0;32m   1862\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1863\u001b[0m   def evaluate_generator(self,\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py\u001b[0m in \u001b[0;36mfit\u001b[1;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)\u001b[0m\n\u001b[0;32m   1098\u001b[0m                 _r=1):\n\u001b[0;32m   1099\u001b[0m               \u001b[0mcallbacks\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mon_train_batch_begin\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1100\u001b[1;33m               \u001b[0mtmp_logs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtrain_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0miterator\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1101\u001b[0m               \u001b[1;32mif\u001b[0m \u001b[0mdata_handler\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshould_sync\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1102\u001b[0m                 \u001b[0mcontext\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0masync_wait\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\def_function.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, *args, **kwds)\u001b[0m\n\u001b[0;32m    826\u001b[0m     \u001b[0mtracing_count\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexperimental_get_tracing_count\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    827\u001b[0m     \u001b[1;32mwith\u001b[0m \u001b[0mtrace\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mTrace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_name\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mtm\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 828\u001b[1;33m       \u001b[0mresult\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    829\u001b[0m       \u001b[0mcompiler\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m\"xla\"\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_experimental_compile\u001b[0m \u001b[1;32melse\u001b[0m \u001b[1;34m\"nonXla\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    830\u001b[0m       \u001b[0mnew_tracing_count\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexperimental_get_tracing_count\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\def_function.py\u001b[0m in \u001b[0;36m_call\u001b[1;34m(self, *args, **kwds)\u001b[0m\n\u001b[0;32m    853\u001b[0m       \u001b[1;31m# In this case we have created variables on the first call, so we run the\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    854\u001b[0m       \u001b[1;31m# defunned version which is guaranteed to never create variables.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 855\u001b[1;33m       \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_stateless_fn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# pylint: disable=not-callable\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    856\u001b[0m     \u001b[1;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_stateful_fn\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    857\u001b[0m       \u001b[1;31m# Release the lock early so that multiple threads can perform the call\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\function.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m   2939\u001b[0m     \u001b[1;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_lock\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   2940\u001b[0m       (graph_function,\n\u001b[1;32m-> 2941\u001b[1;33m        filtered_flat_args) = self._maybe_define_function(args, kwargs)\n\u001b[0m\u001b[0;32m   2942\u001b[0m     return graph_function._call_flat(\n\u001b[0;32m   2943\u001b[0m         filtered_flat_args, captured_inputs=graph_function.captured_inputs)  # pylint: disable=protected-access\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\function.py\u001b[0m in \u001b[0;36m_maybe_define_function\u001b[1;34m(self, args, kwargs)\u001b[0m\n\u001b[0;32m   3356\u001b[0m               call_context_key in self._function_cache.missed):\n\u001b[0;32m   3357\u001b[0m             return self._define_function_with_shape_relaxation(\n\u001b[1;32m-> 3358\u001b[1;33m                 args, kwargs, flat_args, filtered_flat_args, cache_key_context)\n\u001b[0m\u001b[0;32m   3359\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3360\u001b[0m           \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_function_cache\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmissed\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcall_context_key\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\function.py\u001b[0m in \u001b[0;36m_define_function_with_shape_relaxation\u001b[1;34m(self, args, kwargs, flat_args, filtered_flat_args, cache_key_context)\u001b[0m\n\u001b[0;32m   3278\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3279\u001b[0m     graph_function = self._create_graph_function(\n\u001b[1;32m-> 3280\u001b[1;33m         args, kwargs, override_flat_arg_shapes=relaxed_arg_shapes)\n\u001b[0m\u001b[0;32m   3281\u001b[0m     \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_function_cache\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0marg_relaxed\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mrank_only_cache_key\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mgraph_function\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3282\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\function.py\u001b[0m in \u001b[0;36m_create_graph_function\u001b[1;34m(self, args, kwargs, override_flat_arg_shapes)\u001b[0m\n\u001b[0;32m   3204\u001b[0m             \u001b[0marg_names\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg_names\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3205\u001b[0m             \u001b[0moverride_flat_arg_shapes\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0moverride_flat_arg_shapes\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 3206\u001b[1;33m             capture_by_value=self._capture_by_value),\n\u001b[0m\u001b[0;32m   3207\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_function_attributes\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3208\u001b[0m         \u001b[0mfunction_spec\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunction_spec\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\framework\\func_graph.py\u001b[0m in \u001b[0;36mfunc_graph_from_py_func\u001b[1;34m(name, python_func, args, kwargs, signature, func_graph, autograph, autograph_options, add_control_dependencies, arg_names, op_return_value, collections, capture_by_value, override_flat_arg_shapes)\u001b[0m\n\u001b[0;32m    988\u001b[0m         \u001b[0m_\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moriginal_func\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtf_decorator\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0munwrap\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpython_func\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    989\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 990\u001b[1;33m       \u001b[0mfunc_outputs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpython_func\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mfunc_args\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mfunc_kwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    991\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    992\u001b[0m       \u001b[1;31m# invariant: `func_outputs` contains only Tensors, CompositeTensors,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\eager\\def_function.py\u001b[0m in \u001b[0;36mwrapped_fn\u001b[1;34m(*args, **kwds)\u001b[0m\n\u001b[0;32m    632\u001b[0m             \u001b[0mxla_context\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mExit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    633\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 634\u001b[1;33m           \u001b[0mout\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mweak_wrapped_fn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__wrapped__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    635\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mout\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    636\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\framework\\func_graph.py\u001b[0m in \u001b[0;36mwrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    975\u001b[0m           \u001b[1;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m  \u001b[1;31m# pylint:disable=broad-except\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    976\u001b[0m             \u001b[1;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0me\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"ag_error_metadata\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 977\u001b[1;33m               \u001b[1;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mag_error_metadata\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mto_exception\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0me\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    978\u001b[0m             \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    979\u001b[0m               \u001b[1;32mraise\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: in user code:\n\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:805 train_function  *\n        return step_function(self, iterator)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:795 step_function  **\n        outputs = model.distribute_strategy.run(run_step, args=(data,))\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:1259 run\n        return self._extended.call_for_each_replica(fn, args=args, kwargs=kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:2730 call_for_each_replica\n        return self._call_for_each_replica(fn, args, kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\distribute\\distribute_lib.py:3417 _call_for_each_replica\n        return fn(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:788 run_step  **\n        outputs = model.train_step(data)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\training.py:756 train_step\n        y, y_pred, sample_weight, regularization_losses=self.losses)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\engine\\compile_utils.py:203 __call__\n        loss_value = loss_obj(y_t, y_p, sample_weight=sw)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:152 __call__\n        losses = call_fn(y_true, y_pred)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:256 call  **\n        return ag_fn(y_true, y_pred, **self._fn_kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\util\\dispatch.py:201 wrapper\n        return target(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\losses.py:1537 categorical_crossentropy\n        return K.categorical_crossentropy(y_true, y_pred, from_logits=from_logits)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\util\\dispatch.py:201 wrapper\n        return target(*args, **kwargs)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\keras\\backend.py:4833 categorical_crossentropy\n        target.shape.assert_is_compatible_with(output.shape)\n    D:\\code\\environment\\anaconda3\\lib\\site-packages\\tensorflow\\python\\framework\\tensor_shape.py:1134 assert_is_compatible_with\n        raise ValueError(\"Shapes %s and %s are incompatible\" % (self, other))\n\n    ValueError: Shapes (None, None) and (None, None, None, 4) are incompatible\n"
     ]
    }
   ],
   "source": [
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "train_datagen = ImageDataGenerator(\n",
    "    rotation_range=40,       # 图像随机旋转0~40度（最大80度）\n",
    "    width_shift_range=0.2,   # 水平方向上平移\n",
    "    height_shift_range=0.2,  # 垂直方向上平移\n",
    "    shear_range=0.2,         # 随机错切变换的角度\n",
    "    zoom_range=0.2,          # 图像随机缩放的范围\n",
    "    horizontal_flip=True,    # 随机将一半图像水平翻转\n",
    "    fill_mode='nearest'      # 用于填充新创建像素的方法，这些新像素可能来自于旋转或宽度/高度平移\n",
    ")\n",
    "test_datagen = ImageDataGenerator(rescale=1./255) \n",
    "\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "    train_dir, \n",
    "    target_size=(150, 150), \n",
    "    batch_size=32, \n",
    "    class_mode='categorical'\n",
    ")\n",
    "validation_generator = test_datagen.flow_from_directory(\n",
    "    validation_dir, \n",
    "    target_size=(150, 150), \n",
    "    batch_size=32, \n",
    "    class_mode='categorical'\n",
    ")\n",
    "\n",
    "history = model.fit_generator(  \n",
    "    # 这里有时候会报错，把几个epoch调低点就可以了，我也不懂为啥，尽量不要与原来的差太多\n",
    "    train_generator, \n",
    "    steps_per_epoch=50, \n",
    "    epochs=100, \n",
    "    validation_data=validation_generator, \n",
    "    validation_steps=10\n",
    ")\n",
    "\n",
    "# 保存模型\n",
    "# model.save('cats_and_dogs_small_2.h5') \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 绘制新的训练过程中的损失曲线和精度曲线\n",
    "import matplotlib.pyplot as plt \n",
    "\n",
    "acc = history.history['acc'] \n",
    "val_acc = history.history['val_acc'] \n",
    "loss = history.history['loss'] \n",
    "val_loss = history.history['val_loss'] \n",
    "\n",
    "epochs = range(1, len(acc) + 1) \n",
    "\n",
    "plt.plot(epochs, acc, 'bo', label='Training acc') \n",
    "plt.plot(epochs, val_acc, 'b', label='Validation acc') \n",
    "plt.title('Training and Validation accuracy') \n",
    "plt.legend() \n",
    "\n",
    "plt.figure() \n",
    "\n",
    "plt.plot(epochs, loss, 'bo', label='Training loss') \n",
    "plt.plot(epochs, val_loss, 'b', label='Validaion loss') \n",
    "plt.title('Training and validation loss') \n",
    "plt.legend() \n",
    "\n",
    "plt.show() "
   ]
  }
 ]
}